package executor

import (
	"fmt"
	"reflect"

	"gitee.com/zhi/binexpr/ast"
)

// FuncParam is the general wrapper of actual parameters in function expression.
type FuncParam interface {
	ToValue() (any, error)
	ToBool() (bool, error)
	ToUint64() (uint64, error)
	ToInt64() (int64, error)
	ToFloat64() (float64, error)
	ToString() (string, error)
}

// FuncParams is the convient helper for slice of FuncParam.
type FuncParams []FuncParam

// Load the given parameters from the function parameters.
func (pds FuncParams) Load(params ...any) error {
	return loadFuncParams(pds, params...)
}

// FuncDef is the definition structure of a custom function.
type FuncDef struct {
	eval func(params FuncParams) (any, error)
}

// FuncDecl is type constraint of possible types to declare a custom function.
//
// Thre are primarily two forms:
//   - `func(params FuncParams) (any, error)`: the native form of custom function used in the executor.
//   - `reflect.Value` with kind of function: create functions from reflection. It will be slower than the native format.
type FuncDecl interface {
	func(params FuncParams) (any, error) | reflect.Value
}

// NewFuncDef create the custom function from the declaration type.
func NewFuncDef[F FuncDecl](fn F) (*FuncDef, error) {
	var iFn any = fn
	switch f := iFn.(type) {
	case func(params FuncParams) (any, error):
		return newFuncDef(f), nil
	case reflect.Value:
		return newFuncDefFromReflect(f)
	default:
		return nil, nil // impossible
	}
}

// MustNewFuncDef create the custom function from the declaration type or panic.
func MustNewFuncDef[F FuncDecl](fn F) *FuncDef {
	ret, err := NewFuncDef(fn)
	if err != nil {
		panic(err)
	}
	return ret
}

// Executor is the execution context.
type Executor struct {
	scope Scope
}

// New create a new execution context.
func New(scope Scope) *Executor {
	return &Executor{scope: scope}
}

// Scope is the interface of execution scope.
type Scope interface {
	Resolve(name string) (any, error)
}

// ScopeMap is the map implemenation of a scope.
type ScopeMap map[string]any

// Resolve the value of the given name.
func (s ScopeMap) Resolve(name string) (any, error) {
	if s == nil {
		return nil, fmt.Errorf("unknown variable")
	}
	val, ok := s[name]
	if !ok {
		return nil, fmt.Errorf("variable %s not found", name)
	}
	return val, nil
}

// ScopeChain is the chain of the scopes.
type ScopeChain struct {
	scopes []Scope
}

// NewScopeChain create a new scope chain with parent and local scope.
func NewScopeChain(scopes ...Scope) *ScopeChain {
	return &ScopeChain{scopes: scopes}
}

func (s *ScopeChain) Resolve(name string) (any, error) {
	for _, scope := range s.scopes {
		if scope == nil {
			continue
		}
		v, err := scope.Resolve(name)
		if err != nil {
			continue
		}
		return v, nil
	}
	return nil, fmt.Errorf("no such variable")
}

// Execute the expression with the given scope.
func (e *Executor) Execute(expr ast.Expr, scope Scope) (any, error) {
	scope = NewScopeChain(scope, e.scope)
	if expr == nil {
		return nil, fmt.Errorf("unexpected nil expression")
	}
	switch ex := expr.(type) {
	case *ast.UnaryExpr:
		return executeUnaryExpr(e, ex, scope)
	case *ast.BracketExpr:
		return e.Execute(ex.Expr, scope)
	case *ast.ValueExpr:
		return ex.Value, nil
	case *ast.OperatorExpr:
		return executeOperatorExpr(e, ex, scope)
	case *ast.FuncExpr:
		return executeFunctionExpr(e, ex, scope)
	case *ast.VariableExpr:
		return scope.Resolve(ex.Name)
	case *ast.ObjPropExpr:
		return resolveObjPropExpr(e, ex, scope)
	case *ast.ObjIndexExpr:
		return resolveObjIndexExpr(e, ex, scope)
	default:
		return nil, fmt.Errorf("unknown expression type: %s", ex.Type())
	}
}
